home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-05-22 | 50.6 KB | 2,126 lines |
- Newsgroups: comp.sources.misc
- From: Byron Rakitzis <byron@archone.tamu.edu>
- Subject: v20i012: rc - A Plan 9 shell reimplementation, Part03/04
- Message-ID: <1991May22.154305.3230@sparky.IMD.Sterling.COM>
- Date: Wed, 22 May 1991 15:43:05 GMT
- Approved: kent@sparky.imd.sterling.com
- X-Md4-Signature: 5a48e4b485a61186a00b0e9d1cfb6138
-
- Submitted-by: Byron Rakitzis <byron@archone.tamu.edu>
- Posting-number: Volume 20, Issue 12
- Archive-name: rc/part03
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # The tool that generated this appeared in the comp.sources.unix newsgroup;
- # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
- # Contents: CHANGES glob.c hash.c heredoc.c parse.y redir.c tree.c
- # utils.c var.c which.c
- # Wrapped by kent@sparky on Wed May 22 01:21:49 1991
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 3 (of 4)."'
- if test -f 'CHANGES' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'CHANGES'\"
- else
- echo shar: Extracting \"'CHANGES'\" \(2395 characters\)
- sed "s/^X//" >'CHANGES' <<'END_OF_FILE'
- XBugs and changes since version 0.9:
- X
- X-----
- XA pipe with a null first command caused rc to dump core. e.g.,
- X
- X |;
- X-----
- XWhen fifo redirection was used with builtin commands, rc would hang:
- X
- X diff <{echo hi} <{echo bye}
- X
- Xa missing exit() was the cause of all this
- X-----
- XA double backquote operator was added to make inlining of ifs-type material
- Xpossible; thus
- X
- X `` ($ifs / :) command
- X
- Xis the same as
- X
- X ifs=($ifs / :)
- X ` command
- X
- Xonly without the assignment.
- X-----
- XAn "rc error" (e.g., trying to evaluate $()) inside a function messed up
- Xrc's interactive state, i.e., it stopped printing prompts.
- X
- X fn foo { echo $() }
- X
- Xthis was fixed by adding full-fledged exception handling, with a separate
- Xexception stack
- X-----
- Xrc did not raise sigexit from the builtin "exit".
- X-----
- Xrc assumed the existence of getgroups(). Also, a typo in
- Xthe getgroups() code in which.c was fixed.
- X-----
- Xrc now sets $0 to the name of the function being called or the file being
- Xinterpreted by dot.
- X-----
- Xif - else statements were incorrectly exported into the environment
- X-----
- Xrc now has stubs for GNU readline compatability. (compile -DREADLINE)
- X-----
- Xrc used to use a single arena for temporary space which got clobbered if there
- Xwas a command of the form:
- X
- X eval foo ; bar
- X
- Xsince the eval would cause rc to prematurely recycle that memory. I fixed
- Xthis by adding support for multiple arenas.
- X-----
- Xan assignment inside a compound command clobbered the global definition
- Xof a variable:
- X
- X foo=a
- X foo=b { stuff; foo=c; stuff }
- X
- X$foo was set to () after this.
- X-----
- Xrc -e now is much more sh-like. In particular
- X
- X false || echo bletch
- X
- Xnow does the right thing.
- X-----
- Xrc did not print $prompt(2) while scanning heredocs
- X-----
- Xexec with a redirection but without other arguments now does the
- Xsame thing as sh, i.e., it alters rc's own file descriptors
- X-----
- Xthe command
- X
- X eval eval eval [200 times or so] echo hi
- X
- Xwould cause rc to allocate obnoxious amounts of memory. This has
- Xbeen cleaned up.
- X-----
- Xrc how has full support for *all* signals, not just sigquit, sigint
- Xand sighup and sigterm. Additionally, the signal code is generated via
- Xan awk script operating on /usr/include/signal.h, so that rc "learns"
- Xabout the signals of a particular machine at compile time.
- X-----
- Xrc now supports "fn prompt", a function that gets executed before
- Xeach $prompt(1) is printed.
- X-----
- X~ () '' incorrectly returned a true status.
- X-----
- END_OF_FILE
- if test 2395 -ne `wc -c <'CHANGES'`; then
- echo shar: \"'CHANGES'\" unpacked with wrong size!
- fi
- # end of 'CHANGES'
- fi
- if test -f 'glob.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'glob.c'\"
- else
- echo shar: Extracting \"'glob.c'\" \(6192 characters\)
- sed "s/^X//" >'glob.c' <<'END_OF_FILE'
- X/* glob.c: rc's (ugly) globber. This code is not elegant, but it works */
- X
- X#include <sys/types.h>
- X#include "rc.h"
- X#include "glob.h"
- X#include "glom.h"
- X#include "nalloc.h"
- X#include "utils.h"
- X#include "match.h"
- X#include "footobar.h"
- X#include "list.h"
- X#ifdef NODIRENT
- X#include <sys/dir.h>
- X#define dirent direct /* need to get the struct declaraction right */
- X#else
- X#include <dirent.h>
- X#endif
- X
- Xstatic List *dmatch(char *, char *, char *);
- Xstatic List *doglob(char *, char *);
- Xstatic List *lglob(List *, char *, char *, SIZE_T);
- Xstatic List *sort(List *);
- X
- X/*
- X matches a list of words s against a list of patterns p. Returns true iff
- X a pattern in p matches a word in s. null matches null, but otherwise null
- X patterns match nothing.
- X*/
- X
- Xboolean lmatch(List *s, List *p) {
- X List *q;
- X int i;
- X boolean okay;
- X
- X if (s == NULL) {
- X if (p == NULL) /* null matches null */
- X return TRUE;
- X for (; p != NULL; p = p->n) { /* one or more stars match null */
- X if (*p->w != '\0') { /* the null string is a special case; it does *not* match () */
- X okay = TRUE;
- X for (i = 0; p->w[i] != '\0'; i++)
- X if (p->w[i] != '*' || p->m[i] != 1) {
- X okay = FALSE;
- X break;
- X }
- X if (okay)
- X return TRUE;
- X }
- X }
- X return FALSE;
- X }
- X
- X for (; s != NULL; s = s->n)
- X for (q = p; q != NULL; q = q->n)
- X if (match(q->w, q->m, s->w))
- X return TRUE;
- X return FALSE;
- X}
- X
- X/* Matches a pattern p against the contents of directory d */
- X
- Xstatic List *dmatch(char *d, char *p, char *m) {
- X boolean matched = FALSE;
- X List *top, *r;
- X DIR *dirp;
- X struct dirent *dp;
- X /* prototypes for XXXdir functions. comment out if necessary */
- X extern DIR *opendir(const char *);
- X extern struct dirent *readdir(DIR *);
- X extern int closedir(DIR *);
- X
- X if ((dirp = opendir(d)) == NULL)
- X return NULL;
- X
- X top = r = NULL;
- X
- X while ((dp = readdir(dirp)) != NULL)
- X if ((*dp->d_name != '.' || *p == '.') && match(p, m, dp->d_name)) { /* match ^. explicitly */
- X matched = TRUE;
- X if (top == NULL) {
- X top = r = nnew(List);
- X } else {
- X r->n = nnew(List);
- X r = r->n;
- X }
- X r->w = ncpy(dp->d_name);
- X r->m = NULL;
- X }
- X
- X closedir(dirp);
- X
- X if (!matched)
- X return NULL;
- X
- X r->n = NULL;
- X return top;
- X}
- X
- X/*
- X lglob() globs a pattern agains a list of directory roots. e.g., (/tmp /usr /bin) "*"
- X will return a list with all the files in /tmp, /usr, and /bin. NULL on no match.
- X slashcount indicates the number of slashes to stick between the directory and the
- X matched name. e.g., for matching ////tmp/////foo*
- X*/
- X
- Xstatic List *lglob(List *s, char *p, char *m, SIZE_T slashcount) {
- X List *q, *r, *top, foo;
- X static List slash;
- X static SIZE_T slashsize = 0;
- X
- X if (slashcount + 1 > slashsize) {
- X slash.w = ealloc(slashcount + 1);
- X slashsize = slashcount;
- X }
- X slash.w[slashcount] = '\0';
- X while (slashcount > 0)
- X slash.w[--slashcount] = '/';
- X
- X for (top = r = NULL; s != NULL; s = s->n) {
- X q = dmatch(s->w, p, m);
- X if (q != NULL) {
- X foo.w = s->w;
- X foo.m = NULL;
- X foo.n = NULL;
- X if (!(s->w[0] == '/' && s->w[1] == '\0')) /* need to separate */
- X q = concat(&slash, q); /* dir/name with slash */
- X q = concat(&foo, q);
- X if (r == NULL)
- X top = r = q;
- X else
- X r->n = q;
- X while (r->n != NULL)
- X r = r->n;
- X }
- X }
- X return top;
- X}
- X
- X/*
- X Doglob globs a pathname in pattern form against a unix path. Returns the original
- X pattern (cleaned of metacharacters) on failure, or the globbed string(s).
- X*/
- X
- Xstatic List *doglob(char *w, char *m) {
- X static char *dir = NULL, *pattern = NULL, *metadir = NULL, *metapattern = NULL;
- X static SIZE_T dsize = 0;
- X char *d, *p, *md, *mp;
- X SIZE_T psize;
- X char *s = w;
- X List firstdir;
- X List *matched;
- X int slashcount;
- X
- X if ((psize = strlen(w) + 1) > dsize || dir == NULL) {
- X efree(dir); efree(pattern); efree(metadir); efree(metapattern);
- X dir = ealloc(psize);
- X pattern = ealloc(psize);
- X metadir = ealloc(psize);
- X metapattern = ealloc(psize);
- X dsize = psize;
- X }
- X
- X d = dir;
- X p = pattern;
- X md = metadir;
- X mp = metapattern;
- X
- X if (*s == '/') {
- X while (*s == '/')
- X *d++ = *s++, *md++ = *m++;
- X } else {
- X while (*s != '/' && *s != '\0')
- X *d++ = *s++, *md++ = *m++; /* get first directory component */
- X }
- X *d = '\0';
- X
- X /*
- X Special case: no slashes in the pattern, i.e., open the current directory.
- X Remember that w cannot consist of slashes alone (the other way *s could be
- X zero) since doglob gets called iff there's a metacharacter to be matched
- X */
- X if (*s == '\0') {
- X matched = dmatch(".", dir, metadir);
- X goto end;
- X }
- X
- X if (*w == '/') {
- X firstdir.w = dir;
- X firstdir.m = metadir;
- X firstdir.n = NULL;
- X matched = &firstdir;
- X } else {
- X /*
- X we must glob against current directory,
- X since the first character is not a slash.
- X */
- X matched = dmatch(".", dir, metadir);
- X }
- X
- X do {
- X for (slashcount = 0; *s == '/'; s++, m++)
- X slashcount++; /* skip slashes */
- X
- X while (*s != '/' && *s != '\0')
- X *p++ = *s++, *mp++ = *m++; /* get pattern */
- X *p = '\0';
- X
- X matched = lglob(matched, pattern, metapattern, slashcount);
- X
- X p = pattern, mp = metapattern;
- X } while (*s != '\0');
- X
- Xend: if (matched == NULL) {
- X matched = nnew(List);
- X matched->w = w;
- X matched->m = NULL;
- X matched->n = NULL;
- X }
- X return matched;
- X}
- X
- X/*
- X Globs a list; checks to see if each element in the list has a metacharacter. If it
- X does, it is globbed, and the output is sorted.
- X*/
- X
- XList *glob(List *s) {
- X List *top, *r;
- X boolean meta;
- X
- X for (r = s, meta = FALSE; r != NULL; r = r->n)
- X if (r->m != NULL)
- X meta = TRUE;
- X
- X if (!meta)
- X return s; /* don't copy lists with no metacharacters in them */
- X
- X for (top = r = NULL; s != NULL; s = s->n) {
- X if (s->m == NULL) { /* no metacharacters; just tack on to the return list */
- X if (top == NULL) {
- X top = r = nnew(List);
- X } else {
- X r->n = nnew(List);
- X r = r->n;
- X }
- X r->w = s->w;
- X } else {
- X if (top == NULL)
- X top = r = sort(doglob(s->w, s->m));
- X else
- X r->n = sort(doglob(s->w, s->m));
- X while (r->n != NULL)
- X r = r->n;
- X }
- X }
- X
- X r->n = NULL;
- X return top;
- X}
- X
- Xstatic List *sort(List *s) {
- X SIZE_T nel = listnel(s);
- X
- X if (nel > 1) {
- X char **a;
- X List *t;
- X
- X qsort(a = list2array(s, FALSE), nel, sizeof(char *), starstrcmp);
- X
- X for (t = s; t != NULL; t = t->n)
- X t->w = *a++;
- X }
- X
- X return s;
- X}
- END_OF_FILE
- if test 6192 -ne `wc -c <'glob.c'`; then
- echo shar: \"'glob.c'\" unpacked with wrong size!
- fi
- # end of 'glob.c'
- fi
- if test -f 'hash.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'hash.c'\"
- else
- echo shar: Extracting \"'hash.c'\" \(6588 characters\)
- sed "s/^X//" >'hash.c' <<'END_OF_FILE'
- X/* hash.c: hash table support for functions and variables. */
- X
- X/*
- X functions and variables are cached in both internal and external
- X form for performance. Thus a variable which is never "dereferenced"
- X with a $ is passed on to rc's children untouched. This is not so
- X important for variables, but is a big win for functions, where a call
- X to yyparse() is involved.
- X*/
- X
- X#include "rc.h"
- X#include "utils.h"
- X#include "hash.h"
- X#include "list.h"
- X#include "tree.h"
- X#include "footobar.h"
- X
- Xstatic boolean exportable(char *);
- Xstatic int hash(char *, int);
- Xstatic int find(char *, Htab *, int);
- Xstatic void free_fn(Function *);
- X
- XHtab *fp;
- XHtab *vp;
- Xstatic int fused, fsize, vused, vsize;
- Xstatic char **env;
- Xstatic int bozosize;
- Xstatic int envsize;
- Xstatic boolean env_dirty = TRUE;
- Xstatic char *dead = "";
- X
- X#define HASHSIZE 64 /* rc was debugged with HASHSIZE == 2; 64 is about right for normal use */
- X
- Xvoid inithash(void) {
- X int i;
- X Htab *fpp, *vpp;
- X
- X fp = ealloc(sizeof(Htab) * HASHSIZE);
- X vp = ealloc(sizeof(Htab) * HASHSIZE);
- X fused = vused = 0;
- X fsize = vsize = HASHSIZE;
- X
- X for (vpp = vp, fpp = fp, i = 0; i < HASHSIZE; i++, vpp++, fpp++)
- X vpp->name = fpp->name = NULL;
- X}
- X
- X#define ADV() {if ((c = *s++) == '\0') break;}
- X
- X/* hash function courtesy of paul haahr */
- X
- Xstatic int hash(char *s, int size) {
- X int n = 0;
- X int c;
- X
- X while (1) {
- X ADV();
- X n += (c << 17) ^ (c << 11) ^ (c << 5) ^ (c >> 1);
- X ADV();
- X n ^= (c << 14) + (c << 7) + (c << 4) + c;
- X ADV();
- X n ^= (~c << 11) | ((c << 3) ^ (c >> 1));
- X ADV();
- X n -= (c << 16) | (c << 9) | (c << 2) | (c & 3);
- X }
- X
- X if (n < 0)
- X n = ~n;
- X
- X return n & (size - 1); /* need power of 2 size */
- X}
- X
- Xstatic boolean rehash(Htab *ht) {
- X int i,j,size;
- X int newsize,newused;
- X Htab *newhtab;
- X
- X if (ht == fp) {
- X if (fsize > 2 * fused)
- X return FALSE;
- X size = fsize;
- X } else {
- X if (vsize > 2 * vused)
- X return FALSE;
- X size = vsize;
- X }
- X
- X
- X newsize = 2 * size;
- X newhtab = ealloc(newsize * sizeof(Htab));
- X for (i = 0; i < newsize; i++)
- X newhtab[i].name = NULL;
- X
- X for (i = newused = 0; i < size; i++)
- X if (ht[i].name != NULL && ht[i].name != dead) {
- X newused++;
- X j = hash(ht[i].name, newsize);
- X while (newhtab[j].name != NULL) {
- X j++;
- X j &= (newsize - 1);
- X }
- X newhtab[j].name = ht[i].name;
- X newhtab[j].p = ht[i].p;
- X }
- X
- X if (ht == fp) {
- X fused = newused;
- X fp = newhtab;
- X fsize = newsize;
- X } else {
- X vused = newused;
- X vp = newhtab;
- X vsize = newsize;
- X }
- X
- X efree(ht);
- X return TRUE;
- X}
- X
- X#define varfind(s) find(s,vp,vsize)
- X#define fnfind(s) find(s,fp,fsize)
- X
- Xstatic int find(char *s, Htab *ht, int size) {
- X int h = hash(s, size);
- X
- X while (ht[h].name != NULL && !streq(ht[h].name,s)) {
- X h++;
- X h &= size - 1;
- X }
- X
- X return h;
- X}
- X
- Xvoid *lookup(char *s, Htab *ht) {
- X int h = find(s, ht, ht == fp ? fsize : vsize);
- X
- X return (ht[h].name == NULL) ? NULL : ht[h].p;
- X}
- X
- XFunction *get_fn_place(char *s) {
- X int h = fnfind(s);
- X
- X env_dirty = TRUE;
- X
- X if (fp[h].name == NULL) {
- X if (rehash(fp))
- X h = fnfind(s);
- X fused++;
- X fp[h].name = ecpy(s);
- X fp[h].p = enew(Function);
- X } else
- X free_fn(fp[h].p);
- X
- X return fp[h].p;
- X}
- X
- XVariable *get_var_place(char *s, boolean stack) {
- X Variable *new;
- X int h = varfind(s);
- X
- X env_dirty = TRUE;
- X
- X if (vp[h].name == NULL) {
- X if (rehash(vp))
- X h = varfind(s);
- X vused++;
- X vp[h].name = ecpy(s);
- X strcpy(vp[h].name, s);
- X vp[h].p = enew(Variable);
- X ((Variable *)vp[h].p)->n = NULL;
- X return vp[h].p;
- X } else {
- X if (stack) { /* increase the stack by 1 */
- X new = enew(Variable);
- X new->n = vp[h].p;
- X return vp[h].p = new;
- X } else { /* trample the top of the stack */
- X new = vp[h].p;
- X efree(new->extdef);
- X listfree(new->def);
- X return new;
- X }
- X }
- X}
- X
- Xvoid delete_fn(char *s) {
- X int h = fnfind(s);
- X
- X if (fp[h].name == NULL)
- X return; /* not found */
- X
- X env_dirty = TRUE;
- X
- X free_fn(fp[h].p);
- X efree(fp[h].p);
- X efree(fp[h].name);
- X if (fp[h+1].name == NULL) {
- X --fused;
- X fp[h].name = NULL;
- X } else {
- X fp[h].name = dead;
- X }
- X}
- X
- Xvoid delete_var(char *s, boolean stack) {
- X int h = varfind(s);
- X Variable *v;
- X
- X if (vp[h].name == NULL)
- X return; /* not found */
- X
- X env_dirty = TRUE;
- X
- X v = vp[h].p;
- X efree(v->extdef);
- X listfree(v->def);
- X
- X if (v->n != NULL) { /* This is the top of a stack */
- X if (stack) { /* pop */
- X vp[h].p = v->n;
- X efree(v);
- X } else { /* else just empty */
- X v->extdef = NULL;
- X v->def = NULL;
- X }
- X } else { /* needs to be removed from the hash table */
- X efree(v);
- X efree(vp[h].name);
- X if (vp[h+1].name == NULL) {
- X --vused;
- X vp[h].name = NULL;
- X } else {
- X vp[h].name = dead;
- X }
- X }
- X}
- X
- Xstatic void free_fn(Function *f) {
- X treefree(f->def);
- X efree(f->extdef);
- X}
- X
- Xvoid initenv(char **envp) {
- X int n;
- X
- X for (n = 0; envp[n] != NULL; n++)
- X ;
- X n++; /* one for the null terminator */
- X
- X if (n < HASHSIZE)
- X n = HASHSIZE;
- X
- X env = ealloc((envsize = 2 * n) * sizeof (char *));
- X
- X for (; *envp != NULL; envp++)
- X if (strncmp(*envp, "fn_", sizeof("fn_") - 1) == 0)
- X fnassign_string(*envp);
- X else
- X if (!varassign_string(*envp)) /* add to bozo env */
- X env[bozosize++] = *envp;
- X}
- X
- Xstatic boolean exportable(char *s) {
- X int i;
- X static char *notforexport[] = {
- X "sighup", "sigint", "sigquit", "sigterm", "sigexit",
- X "apid", "pid", "apid", "*", "ifs"
- X };
- X
- X for (i = 0; i < arraysize(notforexport); i++)
- X if (streq(s,notforexport[i]))
- X return FALSE;
- X return TRUE;
- X}
- X
- Xchar **makeenv(void) {
- X int ep, i;
- X char *v;
- X
- X if (!env_dirty)
- X return env;
- X
- X env_dirty = FALSE;
- X ep = bozosize;
- X
- X if (vsize + fsize + 1 + bozosize > envsize) {
- X envsize = 2 * (bozosize + vsize + fsize + 1);
- X env = erealloc(env, envsize * sizeof(char *));
- X }
- X
- X for (i = 0; i < vsize; i++) {
- X if (vp[i].name == NULL || !exportable(vp[i].name))
- X continue;
- X v = varlookup_string(vp[i].name);
- X if (v != NULL)
- X env[ep++] = v;
- X }
- X for (i = 0; i < fsize; i++) {
- X if (fp[i].name == NULL || !exportable(fp[i].name))
- X continue;
- X env[ep++] = fnlookup_string(fp[i].name);
- X }
- X env[ep] = NULL;
- X return env;
- X}
- X
- Xvoid whatare_all_vars(void) {
- X int i;
- X List *s,*t;
- X
- X for (i = 0; i < vsize; i++)
- X if (vp[i].name != NULL && (s = varlookup(vp[i].name)) != NULL) {
- X fprint(1,"%s=%s", vp[i].name, (s->n == NULL ? "" : "("));
- X for (t = s; t->n != NULL; t = t->n)
- X fprint(1,"%s ",strprint(t->w, FALSE, TRUE));
- X fprint(1,"%s%s\n",strprint(t->w, FALSE, TRUE), (s->n == NULL ? "" : ")"));
- X }
- X for (i = 0; i < fsize; i++)
- X if (fp[i].name != NULL)
- X fprint(1,"fn %s {%s}\n", fp[i].name, ptree(fnlookup(fp[i].name)));
- X}
- X
- X/* fake getenv() for readline() follows: */
- X
- X#ifdef READLINE
- Xchar *getenv(char *name) {
- X List *s;
- X
- X if (name == NULL || (s = varlookup(name)) == NULL)
- X return NULL;
- X
- X return s->w;
- X}
- X#endif
- X
- END_OF_FILE
- if test 6588 -ne `wc -c <'hash.c'`; then
- echo shar: \"'hash.c'\" unpacked with wrong size!
- fi
- # end of 'hash.c'
- fi
- if test -f 'heredoc.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'heredoc.c'\"
- else
- echo shar: Extracting \"'heredoc.c'\" \(3252 characters\)
- sed "s/^X//" >'heredoc.c' <<'END_OF_FILE'
- X/* heredoc.c: heredoc slurping is done here */
- X
- X#include "rc.h"
- X#include "lex.h"
- X#include "utils.h"
- X#include "nalloc.h"
- X#include "heredoc.h"
- X#include "tree.h"
- X#include "input.h"
- X#include "hash.h"
- X#include "glom.h"
- X
- Xstruct Hq {
- X Node *doc;
- X char *name;
- X Hq *n;
- X} *hq;
- X
- Xstatic SIZE_T j, bufsize;
- X
- X/* a stupid realloc for nalloc */
- X
- Xstatic char *renalloc(char *buf, SIZE_T n) {
- X
- X while (n >= bufsize - 2)
- X bufsize *= 2;
- X
- X return memcpy(nalloc(bufsize), buf, j);
- X}
- X
- X/*
- X read in a heredocument. A clever trick: skip over any partially matched end-of-file
- X marker storing only the number of characters matched. If the whole marker is matched,
- X return from readheredoc(). If only part of the marker is matched, copy that part into
- X the heredocument.
- X*/
- X
- Xstatic char *readheredoc(char *eof) {
- X int c;
- X SIZE_T i, markerlen, varsize = 0;
- X char *buf, *varname = NULL, *newvarname;
- X boolean quoted = FALSE;
- X List *var;
- X
- X if (*eof == '\'') {
- X quoted = TRUE;
- X eof++;
- X }
- X
- X markerlen = strlen(eof);
- X
- X buf = nalloc(bufsize = 512);
- X j = 0;
- X
- X while (1) {
- X print_prompt2();
- X
- X for (i = 0, c = gchar(); eof[i] == c && i < markerlen; i++) /* match the eof marker */
- X c = gchar();
- X
- X if (i == markerlen && c == '\n') { /* if the whole marker was matched, return */
- X buf[j] = 0;
- X return buf;
- X }
- X
- X if (i > 0) { /* else copy the partially matched marker into the heredocument */
- X if (i + j >= bufsize - 2)
- X buf = renalloc(buf, i + j);
- X memcpy(buf + j, eof, i);
- X j += i;
- X }
- X
- X for (; c != '\n'; c = gchar()) {
- X if (c == EOF)
- X scanerror("EOF inside heredoc");
- X if (j >= bufsize - 2)
- X buf = renalloc(buf, j);
- X if (quoted || c != '$') {
- X buf[j++] = c;
- X continue; /* avoid an extra level of indentation */
- X }
- X if ((c = gchar()) == '$' || dnw[c]) {
- X if (c != '$') /* $$ quotes $, but $<nonalnum> is transcribed literally */
- X buf[j++] = '$';
- X buf[j++] = c;
- X } else {
- X if (varsize == 0)
- X varname = nalloc(varsize = 16);
- X for (i = 0; !dnw[c]; i++, c = gchar()) {
- X if (i >= varsize - 1) {
- X newvarname = nalloc(varsize *= 4);
- X memcpy(newvarname, varname, i);
- X varname = newvarname;
- X }
- X varname[i] = c;
- X }
- X varname[i] = '\0';
- X if (c != '^')
- X ugchar(c);
- X var = varlookup(varname);
- X if (varname[0] == '*' && varname[1] == '\0') /* skip $0 */
- X var = var->n;
- X if ((var = flatten(var)) != NULL) {
- X i = strlen(var->w);
- X while (j + i >= bufsize - 2)
- X buf = renalloc(buf, i + j);
- X memcpy(buf + j, var->w, i);
- X j += i;
- X }
- X }
- X }
- X buf[j++] = c;
- X }
- X}
- X
- X/* read in heredocs when yyparse hits a newline. called from yyparse */
- X
- Xvoid heredoc(int end) {
- X if (end && hq != NULL)
- X rc_error("EOF on command line with heredoc");
- X
- X for (; hq != NULL; hq = hq->n) {
- X hq->doc->u[0].i = HERESTRING;
- X hq->doc->u[2].p = newnode(rWORD,readheredoc(hq->name), NULL);
- X }
- X}
- X
- X/* queue pending heredocs into a queue. called from yyparse */
- X
- Xvoid qdoc(Node *name, Node *n) {
- X Hq *new;
- X
- X if (name->type != rWORD)
- X scanerror("eof-marker must be a single word");
- X
- X if (hq == NULL) {
- X new = hq = nnew(Hq);
- X } else {
- X for (new = hq; new->n != NULL; new = new->n)
- X ;
- X new->n = nnew(Hq);
- X new = new->n;
- X }
- X
- X new->name = name->u[0].s;
- X new->doc = n;
- X new->n = NULL;
- X}
- END_OF_FILE
- if test 3252 -ne `wc -c <'heredoc.c'`; then
- echo shar: \"'heredoc.c'\" unpacked with wrong size!
- fi
- # end of 'heredoc.c'
- fi
- if test -f 'parse.y' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'parse.y'\"
- else
- echo shar: Extracting \"'parse.y'\" \(5173 characters\)
- sed "s/^X//" >'parse.y' <<'END_OF_FILE'
- X/* parse.y */
- X
- X/*
- X * Adapted from rc grammar, v10 manuals, volume 2.
- X */
- X
- X%{
- X#include "stddef.h"
- X#include "node.h"
- X#include "lex.h"
- X#include "tree.h"
- X#include "heredoc.h"
- X#include "parse.h"
- X#include "utils.h"
- X#undef NULL
- X#define NULL 0
- X#undef lint
- X#define lint /* hush up gcc -Wall, leave out the dumb sccsid's. */
- Xstatic Node *star, *nolist;
- XNode *parsetree; /* not using yylval because bison declares it as an auto */
- X%}
- X
- X%term BANG DUP ELSE END FN FOR HUH IF IN LBRACK PIPE RBRACK
- X%term REDIR STAR SUB SUBSHELL SWITCH TWIDDLE WHILE WORD
- X
- X%left IF WHILE FOR SWITCH ')' ELSE
- X%left ANDAND OROR
- X%left BANG SUBSHELL
- X%left PIPE
- X%left '^'
- X%right '$' COUNT FLAT
- X%left SUB
- X%left '`' BACKBACK
- X
- X%union {
- X struct Node *node;
- X struct Redir redir;
- X struct Pipe pipe;
- X struct Dup dup;
- X struct Word word;
- X char *keyword;
- X}
- X
- X%type <redir> REDIR
- X%type <pipe> PIPE
- X%type <dup> DUP
- X%type <word> WORD
- X%type <keyword> keyword
- X%type <node> assign body brace cmd cmdsa cmdsan comword epilog
- X first line paren redir simple iftail word words
- X
- X%start rc
- X
- X%%
- X
- Xrc : line end { parsetree = $1; return 1; }
- X | error end { yyerrok; parsetree = NULL; return -1; }
- X
- X/* an rc line may end in end-of-file as well as newline, e.g., rc -c 'ls' */
- Xend : END /* EOF */ { heredoc(1); } /* flag error if there is a heredoc in the queue */
- X | '\n' { heredoc(0); } /* get heredoc on \n */
- X
- X/* a cmdsa is a command followed by ampersand or newline (used in "line" and "body") */
- Xcmdsa : cmd ';'
- X | cmd '&' { $$ = ($1 != NULL ? newnode(NOWAIT,$1) : $1); }
- X
- X/* a line is a single command, or a command terminated by ; or & followed by a line (recursive) */
- Xline : cmd
- X | cmdsa line { $$ = ($1 != NULL ? newnode(BODY,$1,$2) : $2); }
- X
- X/* a body is like a line, only commands may also be terminated by newline */
- Xbody : cmd
- X | cmdsan body { $$ = ($1 != NULL ? newnode(BODY,$1,$2) : $2); }
- X
- Xcmdsan : cmdsa
- X | cmd '\n' { $$ = $1; heredoc(0); } /* get h.d. on \n */
- X
- Xbrace : '{' body '}' { $$ = $2; }
- X
- Xparen : '(' body ')' { $$ = $2; }
- X
- Xassign : first '=' word { $$ = newnode(ASSIGN,$1,$3); }
- X
- Xepilog : { $$ = NULL; }
- X | redir epilog { $$ = newnode(EPILOG,$1,$2); }
- X
- X/* a redirection is a dup (e.g., >[1=2]) or a file redirection. (e.g., > /dev/null) */
- Xredir : DUP { $$ = newnode(rDUP,$1.type,$1.left,$1.right); }
- X | REDIR word { $$ = newnode(rREDIR,$1.type,$1.fd,$2);
- X if ($1.type == HEREDOC) qdoc($2, $$); /* queue heredocs up */
- X }
- X
- X/* the tail of an if statement can be a command, or a braced command followed by else on the same line */
- Xiftail : cmd %prec IF
- X | brace ELSE cmd { $$ = newnode(rELSE,$1,$3); }
- X
- X/* skipnl() skips newlines and comments between an operator and its operand */
- Xcmd : { $$ = NULL; }
- X | simple
- X | brace epilog { $$ = ($2 == NULL ? $1 : newnode(BRACE,$1,$2)); }
- X | IF paren { skipnl(); } iftail { $$ = newnode(rIF,$2,$4); }
- X | FOR '(' word IN words ')' { skipnl(); } cmd { $$ = newnode(FORIN,$3,$5,$8); }
- X | FOR '(' word ')' { skipnl(); } cmd { $$ = newnode(FORIN,$3,star,$6); }
- X | WHILE paren { skipnl(); } cmd { $$ = newnode(rWHILE,$2,$4); }
- X | SWITCH '(' word ')' { skipnl(); } brace { $$ = newnode(rSWITCH,$3,$6); }
- X | TWIDDLE word words { $$ = newnode(MATCH,$2,$3); }
- X | cmd ANDAND { skipnl(); } cmd { $$ = ($1 != NULL ? newnode(rANDAND,$1,$4) : $4); }
- X | cmd OROR { skipnl(); } cmd { $$ = ($1 != NULL ? newnode(rOROR,$1,$4) : $4); }
- X | cmd PIPE { skipnl(); } cmd { $$ = newnode(rPIPE,$2.left,$2.right,$1,$4); }
- X | redir cmd %prec BANG { $$ = ($2 != NULL ? newnode(PRE,$1,$2) : $1); }
- X | assign cmd %prec BANG { $$ = ($2 != NULL ? newnode(PRE,$1,$2) : $1); }
- X | BANG cmd { $$ = newnode(rBANG,$2); }
- X | SUBSHELL cmd { $$ = newnode(rSUBSHELL,$2); }
- X | FN words brace { $$ = newnode(NEWFN,$2,$3); }
- X | FN words { $$ = newnode(RMFN,$2); }
- X
- Xsimple : first
- X | simple word { $$ = ($2 != NULL ? newnode(ARGS,$1,$2) : $1); }
- X | simple redir { $$ = newnode(ARGS,$1,$2); }
- X
- Xfirst : comword
- X | first '^' word { $$ = newnode(CONCAT,$1,$3); }
- X
- Xword : comword
- X | keyword { $$ = newnode(rWORD,$1, NULL); }
- X | word '^' word { $$ = newnode(CONCAT,$1,$3); }
- X
- Xcomword : '$' word { $$ = newnode(VAR,$2); }
- X | '$' word SUB words ')' { $$ = newnode(VARSUB,$2,$4); }
- X | COUNT word { $$ = newnode(rCOUNT,$2); }
- X | FLAT word { $$ = newnode(rFLAT, $2); }
- X | WORD { $$ = newnode(rWORD,$1.w, $1.m); }
- X | '`' word { $$ = newnode(BACKQ,nolist,$2); }
- X | '`' brace { $$ = newnode(BACKQ,nolist,$2); }
- X | BACKBACK word brace { $$ = newnode(BACKQ,$2,$3); }
- X | BACKBACK word word { $$ = newnode(BACKQ,$2,$3); }
- X | '(' words ')' { $$ = $2; }
- X | REDIR brace { $$ = newnode(NMPIPE,$1.type,$1.fd,$2); }
- X
- Xkeyword : FOR { $$ = "for"; }
- X | IN { $$ = "in"; }
- X | WHILE { $$ = "while"; }
- X | IF { $$ = "if"; }
- X | SWITCH { $$ = "switch"; }
- X | FN { $$ = "fn"; }
- X | ELSE { $$ = "else"; }
- X | TWIDDLE { $$ = "~"; }
- X | BANG { $$ = "!"; }
- X | SUBSHELL { $$ = "@"; }
- X
- Xwords : { $$ = NULL; }
- X | words word { $$ = ($1 != NULL ? ($2 != NULL ? newnode(LAPPEND,$1,$2) : $1) : $2); }
- X
- X%%
- X
- Xvoid initparse() {
- X star = treecpy(newnode(VAR,newnode(rWORD,"*",NULL)), ealloc);
- X nolist = treecpy(newnode(VAR,newnode(rWORD,"ifs",NULL)), ealloc);
- X}
- END_OF_FILE
- if test 5173 -ne `wc -c <'parse.y'`; then
- echo shar: \"'parse.y'\" unpacked with wrong size!
- fi
- # end of 'parse.y'
- fi
- if test -f 'redir.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'redir.c'\"
- else
- echo shar: Extracting \"'redir.c'\" \(2480 characters\)
- sed "s/^X//" >'redir.c' <<'END_OF_FILE'
- X/* redir.c: code for opening files and piping heredocs after fork but before exec. */
- X
- X#include "rc.h"
- X#include "lex.h"
- X#include "glom.h"
- X#include "glob.h"
- X#include "open.h"
- X#include "exec.h"
- X#include "utils.h"
- X#include "redir.h"
- X#include "hash.h"
- X
- X/*
- X Walk the redirection queue, and open files and dup2 to them. Also, here-documents are treated
- X here by dumping them down a pipe. (this should make here-documents fast on systems with lots
- X of memory which do pipes right. Under sh, a file is copied to /tmp, and then read out of /tmp
- X again. I'm interested in knowing how much faster, say, shar runs when unpacking when invoked
- X with rc instead of sh. On my sun4/280, it runs in about 60-75% of the time of sh for unpacking
- X the rc source distribution.)
- X*/
- X
- Xvoid doredirs() {
- X List *fname;
- X int fd, p[2];
- X Rq *r;
- X
- X for (r = redirq; r != NULL; r = r->n) {
- X switch(r->r->type) {
- X default:
- X fprint(2,"%d: bad node in doredirs\n", r->r->type);
- X exit(1);
- X /* NOTREACHED */
- X case rREDIR:
- X if (r->r->u[0].i == HERESTRING) {
- X fname = flatten(glom(r->r->u[2].p)); /* fname is really a string */
- X if (fname == NULL) {
- X close(r->r->u[1].i); /* feature? */
- X break;
- X }
- X if (pipe(p) < 0) {
- X uerror("pipe");
- X exit(1);
- X }
- X switch (fork()) {
- X case -1:
- X uerror("fork");
- X exit(1);
- X /* NOTREACHED */
- X case 0: /* child writes to pipe */
- X setsigdefaults();
- X close(p[0]);
- X writeall(p[1], fname->w, strlen(fname->w));
- X exit(0);
- X /* NOTREACHED */
- X default:
- X close(p[1]);
- X if (dup2(p[0], r->r->u[1].i) < 0) {
- X uerror("dup");
- X exit(1);
- X }
- X close(p[0]);
- X }
- X } else {
- X fname = glob(glom(r->r->u[2].p));
- X if (fname == NULL) {
- X fprint(2,"null filename in redirection\n");
- X exit(1);
- X }
- X if (fname->n != NULL) {
- X fprint(2,"multi-word filename in redirection\n");
- X exit(1);
- X }
- X switch (r->r->u[0].i) {
- X default:
- X fprint(2,"doredirs: this can't happen\n");
- X exit(1);
- X /* NOTREACHED */
- X case CREATE: case APPEND: case FROM:
- X fd = rc_open(fname->w, r->r->u[0].i);
- X break;
- X }
- X if (fd < 0) {
- X uerror(fname->w);
- X exit(1);
- X }
- X if (dup2(fd, r->r->u[1].i) < 0) {
- X uerror("dup");
- X exit(1);
- X }
- X close(fd);
- X }
- X break;
- X case rDUP:
- X if (r->r->u[2].i == -1)
- X close(r->r->u[1].i);
- X else if (dup2(r->r->u[2].i, r->r->u[1].i) < 0) {
- X uerror("dup");
- X exit(1);
- X }
- X }
- X }
- X redirq = NULL;
- X}
- END_OF_FILE
- if test 2480 -ne `wc -c <'redir.c'`; then
- echo shar: \"'redir.c'\" unpacked with wrong size!
- fi
- # end of 'redir.c'
- fi
- if test -f 'tree.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'tree.c'\"
- else
- echo shar: Extracting \"'tree.c'\" \(4613 characters\)
- sed "s/^X//" >'tree.c' <<'END_OF_FILE'
- X/* tree.c: functions for manipulating parse-trees. (create, copy, delete) */
- X
- X#include <stdarg.h>
- X#include "rc.h"
- X#include "tree.h"
- X#include "utils.h"
- X#include "nalloc.h"
- X
- X/* make a new node, pass it back to yyparse. Used to generate the parsetree. */
- X
- XNode *newnode(int /*enum nodetype*/ t,...) {
- X va_list ap;
- X Node *n;
- X
- X va_start(ap,t);
- X
- X switch (t) {
- X default:
- X fprint(2,"newnode: this can't happen\n");
- X exit(1);
- X /* NOTREACHED */
- X case rDUP:
- X n = nalloc(offsetof(Node, u[3]));
- X n->u[0].i = va_arg(ap, int);
- X n->u[1].i = va_arg(ap, int);
- X n->u[2].i = va_arg(ap, int);
- X break;
- X case rWORD:
- X n = nalloc(offsetof(Node, u[2]));
- X n->u[0].s = va_arg(ap, char *);
- X n->u[1].s = va_arg(ap, char *);
- X break;
- X case rBANG: case NOWAIT:
- X case rCOUNT: case rFLAT: case RMFN: case rSUBSHELL:
- X case VAR:
- X n = nalloc(offsetof(Node, u[1]));
- X n->u[0].p = va_arg(ap, Node *);
- X break;
- X case rANDAND: case ASSIGN: case BACKQ: case BODY: case BRACE: case CONCAT:
- X case rELSE: case EPILOG: case rIF: case NEWFN:
- X case rOROR: case PRE: case ARGS: case rSWITCH:
- X case MATCH: case VARSUB: case rWHILE: case LAPPEND:
- X n = nalloc(offsetof(Node, u[2]));
- X n->u[0].p = va_arg(ap, Node *);
- X n->u[1].p = va_arg(ap, Node *);
- X break;
- X case FORIN:
- X n = nalloc(offsetof(Node, u[3]));
- X n->u[0].p = va_arg(ap, Node *);
- X n->u[1].p = va_arg(ap, Node *);
- X n->u[2].p = va_arg(ap, Node *);
- X break;
- X case rPIPE:
- X n = nalloc(offsetof(Node, u[4]));
- X n->u[0].i = va_arg(ap, int);
- X n->u[1].i = va_arg(ap, int);
- X n->u[2].p = va_arg(ap, Node *);
- X n->u[3].p = va_arg(ap, Node *);
- X break;
- X case rREDIR:
- X case NMPIPE:
- X n = nalloc(offsetof(Node, u[3]));
- X n->u[0].i = va_arg(ap, int);
- X n->u[1].i = va_arg(ap, int);
- X n->u[2].p = va_arg(ap, Node *);
- X break;
- X }
- X n->type = t;
- X va_end(ap);
- X return n;
- X}
- X
- X/* copy a tree to malloc space. Used when storing the definition of a function */
- X
- XNode *treecpy(Node *s, void (*alloc(SIZE_T))) {
- X Node *n;
- X
- X if (s == NULL)
- X return NULL;
- X
- X switch (s->type) {
- X default:
- X fprint(2,"treecpy: this can't happen\n");
- X exit(1);
- X /* NOTREACHED */
- X case rDUP:
- X n = alloc(offsetof(Node, u[3]));
- X n->u[0].i = s->u[0].i;
- X n->u[1].i = s->u[1].i;
- X n->u[2].i = s->u[2].i;
- X break;
- X case rWORD:
- X n = alloc(offsetof(Node, u[2]));
- X n->u[0].s = ecpy(s->u[0].s);
- X if (s->u[1].s != NULL) {
- X SIZE_T i = strlen(s->u[0].s);
- X
- X n->u[1].s = alloc(i);
- X memcpy(n->u[1].s, s->u[1].s, i);
- X } else
- X n->u[1].s = NULL;
- X break;
- X case rBANG: case NOWAIT:
- X case rCOUNT: case rFLAT: case RMFN: case rSUBSHELL: case VAR:
- X n = alloc(offsetof(Node, u[1]));
- X n->u[0].p = treecpy(s->u[0].p, alloc);
- X break;
- X case rANDAND: case ASSIGN: case BACKQ: case BODY: case BRACE: case CONCAT:
- X case rELSE: case EPILOG: case rIF: case NEWFN:
- X case rOROR: case PRE: case ARGS: case rSWITCH:
- X case MATCH: case VARSUB: case rWHILE: case LAPPEND:
- X n = alloc(offsetof(Node, u[2]));
- X n->u[0].p = treecpy(s->u[0].p, alloc);
- X n->u[1].p = treecpy(s->u[1].p, alloc);
- X break;
- X case FORIN:
- X n = alloc(offsetof(Node, u[3]));
- X n->u[0].p = treecpy(s->u[0].p, alloc);
- X n->u[1].p = treecpy(s->u[1].p, alloc);
- X n->u[2].p = treecpy(s->u[2].p, alloc);
- X break;
- X case rPIPE:
- X n = alloc(offsetof(Node, u[4]));
- X n->u[0].i = s->u[0].i;
- X n->u[1].i = s->u[1].i;
- X n->u[2].p = treecpy(s->u[2].p, alloc);
- X n->u[3].p = treecpy(s->u[3].p, alloc);
- X break;
- X case rREDIR:
- X case NMPIPE:
- X n = alloc(offsetof(Node, u[3]));
- X n->u[0].i = s->u[0].i;
- X n->u[1].i = s->u[1].i;
- X n->u[2].p = treecpy(s->u[2].p, alloc);
- X break;
- X }
- X n->type = s->type;
- X return n;
- X}
- X
- X/* free a function definition that is no longer needed */
- X
- Xvoid treefree(Node *s) {
- X if (s == NULL)
- X return;
- X switch (s->type) {
- X case rDUP:
- X break;
- X case rWORD:
- X efree(s->u[0].s);
- X efree(s->u[1].s);
- X break;
- X case rBANG: case NOWAIT:
- X case rCOUNT: case rFLAT: case RMFN:
- X case rSUBSHELL: case VAR:
- X treefree(s->u[0].p);
- X efree(s->u[0].p);
- X break;
- X case rANDAND: case ASSIGN: case BACKQ: case BODY: case BRACE: case CONCAT:
- X case rELSE: case EPILOG: case rIF: case NEWFN:
- X case rOROR: case PRE: case ARGS:
- X case rSWITCH: case MATCH: case VARSUB: case rWHILE:
- X case LAPPEND:
- X treefree(s->u[1].p);
- X treefree(s->u[0].p);
- X efree(s->u[1].p);
- X efree(s->u[0].p);
- X break;
- X case FORIN:
- X treefree(s->u[2].p);
- X treefree(s->u[1].p);
- X treefree(s->u[0].p);
- X efree(s->u[2].p);
- X efree(s->u[1].p);
- X efree(s->u[0].p);
- X break;
- X case rPIPE:
- X treefree(s->u[2].p);
- X treefree(s->u[3].p);
- X efree(s->u[2].p);
- X efree(s->u[3].p);
- X break;
- X case rREDIR:
- X case NMPIPE:
- X treefree(s->u[2].p);
- X efree(s->u[2].p);
- X break;
- X default:
- X fprint(2,"treefree: this can't happen\n");
- X exit(1);
- X }
- X}
- END_OF_FILE
- if test 4613 -ne `wc -c <'tree.c'`; then
- echo shar: \"'tree.c'\" unpacked with wrong size!
- fi
- # end of 'tree.c'
- fi
- if test -f 'utils.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'utils.c'\"
- else
- echo shar: Extracting \"'utils.c'\" \(5186 characters\)
- sed "s/^X//" >'utils.c' <<'END_OF_FILE'
- X/* utils.c: general utility functions like fprint, ealloc etc. */
- X
- X#include <stdarg.h>
- X#include <errno.h>
- X#include <setjmp.h>
- X#include <signal.h>
- X#include "rc.h"
- X#include "utils.h"
- X#include "nalloc.h"
- X#include "status.h"
- X#include "input.h"
- X#include "except.h"
- X#include "lex.h" /* import char nw[]; used by strprint to see if it needs to quote a word */
- X#include "walk.h"
- X
- Xstatic void dprint(va_list, char *, char *);
- Xstatic int n2u(char *, int);
- X
- X/* exception handlers */
- X
- Xvoid rc_error(char *s) {
- X if (s != NULL) {
- X if (interactive)
- X fprint(2,"%s\n",s);
- X else
- X fprint(2,"line %d: %s\n", lineno - 1, s);
- X }
- X set(FALSE);
- X redirq = NULL;
- X cond = FALSE; /* no longer inside conditional */
- X empty_fifoq();
- X rc_raise(ERROR);
- X}
- X
- Xvoid sig(int s) {
- X signal(SIGINT, sig); /* some unices require re-signaling */
- X
- X if (errno == EINTR)
- X return; /* allow wait() to complete */
- X
- X fprint(2,"\n"); /* this is the newline you see when you hit ^C while typing a command */
- X redirq = NULL;
- X cond = FALSE;
- X empty_fifoq();
- X rc_raise(ERROR);
- X}
- X
- X/* our perror */
- X
- Xvoid uerror(char *s) {
- X extern int sys_nerr;
- X extern char *sys_errlist[];
- X
- X if (errno > sys_nerr)
- X return;
- X
- X if (s != NULL)
- X fprint(2,"%s: %s\n",s,sys_errlist[errno]);
- X else
- X fprint(2,"%s\n",sys_errlist[errno]);
- X}
- X
- X/* printing functions */
- X
- Xvoid fprint(int fd, char *f,...) {
- X va_list ap;
- X char str[FPRINT_SIZE];
- X
- X va_start(ap,f);
- X dprint(ap, str, f);
- X va_end(ap);
- X writeall(fd,str,strlen(str));
- X}
- X
- Xchar *sprint(char *b, char *f,...) {
- X va_list ap;
- X
- X va_start(ap, f);
- X dprint(ap, b, f);
- X va_end(ap);
- X return b;
- X}
- X
- Xstatic void dprint(va_list ap, char *strbuf, char *f) {
- X int i;
- X
- X for (i = 0; *f != '\0'; f++) {
- X if (*f != '%') {
- X strbuf[i++] = *f;
- X continue; /* avoid an ugly extra level of indentation */
- X }
- X switch (*++f) {
- X case 'a': {
- X char **a = va_arg(ap, char **);
- X
- X if (*a == NULL)
- X break;
- X strcpy(strbuf + i, *a);
- X i += strlen(*a);
- X while (*++a != NULL) {
- X strbuf[i++] = ' ';
- X strcpy(strbuf + i, *a);
- X i += strlen(*a);
- X }
- X break;
- X }
- X case 'c':
- X strbuf[i++] = va_arg(ap, int);
- X break;
- X case 'd': case 'o': {
- X int v = va_arg(ap, int);
- X int j = 0;
- X int base = (*f == 'd' ? 10 : 8);
- X char num[16];
- X
- X if (v == 0)
- X num[j++] = '0';
- X while (v != 0) {
- X num[j++] = (v % base) + '0';
- X v /= base;
- X }
- X while (--j >= 0)
- X strbuf[i++] = num[j];
- X break;
- X }
- X case 's': {
- X char *s = va_arg(ap, char *);
- X while (*s != '\0')
- X strbuf[i++] = *s++;
- X break;
- X }
- X default: /* on format error, just print the bad format */
- X strbuf[i++] = '%';
- X /* FALLTHROUGH */
- X case '%':
- X strbuf[i++] = *f;
- X }
- X }
- X strbuf[i] = '\0';
- X}
- X
- X/* prints a string in rc-quoted form. e.g., a string with spaces in it must be quoted */
- X
- Xchar *strprint(char *s, int quotable, int metaquote) { /* really boolean, but y.tab.c includes utils.h */
- X SIZE_T i,j;
- X char *t;
- X
- X if (*s == '\0')
- X return "''";
- X
- X for (i = 0; s[i] != '\0'; i++)
- X if (nw[s[i]] == 1 && (metaquote || (s[i] != '*' && s[i] != '?' && s[i] != '[')))
- X quotable = TRUE;
- X
- X if (!quotable)
- X return s;
- X
- X for(i = j = 0; s[i] != '\0'; i++, j++)
- X if (s[i] == '\'')
- X j++;
- X
- X t = nalloc(j + 3);
- X
- X t[0] = '\'';
- X
- X for (j = 1, i = 0; s[i] != '\0'; i++, j++) {
- X t[j] = s[i];
- X if (s[i] == '\'')
- X t[++j] = '\'';
- X }
- X
- X t[j++] = '\'';
- X t[j] = '\0';
- X
- X return t;
- X}
- X
- X/* ascii -> unsigned conversion routines. -1 indicates conversion error. */
- X
- Xstatic int n2u(char *s, int base) {
- X int i;
- X
- X for (i = 0; *s != '\0'; s++) {
- X /* small hack with unsigned ints -- one compare for range test */
- X if (((unsigned int) *s) - '0' >= (unsigned int) base)
- X return -1;
- X i = (i * base) + (*s - '0');
- X }
- X return i;
- X}
- X
- X/* decimal -> uint */
- X
- Xint a2u(char *s) {
- X return n2u(s, 10);
- X}
- X
- X/* octal -> uint */
- X
- Xint o2u(char *s) {
- X return n2u(s, 8);
- X}
- X
- X/* memory allocation functions */
- X
- Xvoid *ealloc(SIZE_T n) {
- X char *p = malloc(n);
- X
- X if (p == NULL) {
- X uerror("malloc");
- X rc_exit(1);
- X }
- X
- X return p;
- X}
- X
- Xvoid *erealloc(void *p, SIZE_T n) {
- X p = realloc(p, n);
- X
- X if (p == NULL) {
- X uerror("realloc");
- X rc_exit(1);
- X }
- X
- X return p;
- X}
- X
- Xvoid efree(void *p) {
- X if (p != NULL)
- X free(p);
- X}
- X
- X/* useful functions */
- X
- X/* The last word in portable ANSI: a strcmp wrapper for qsort */
- X
- Xint starstrcmp(const void *s1, const void *s2) {
- X return strcmp(*(char **)s1, *(char **)s2);
- X}
- X
- X/* tests to see if pathname begins with "/", "./", or "../" */
- X
- Xint isabsolute(char *path) {
- X return path[0] == '/' || (path[0] == '.' && (path[1] == '/' || (path[1] == '.' && path[2] == '/')));
- X}
- X
- X/* write a given buffer allowing for partial writes from write(2) */
- X
- Xvoid writeall(int fd, char *buf, SIZE_T remain) {
- X int i;
- X
- X for (i = 0; remain > 0; buf += i, remain -= i)
- X i = write(fd, buf, remain);
- X}
- X
- X/* clear out z bytes from character string s */
- X
- Xvoid clear(char *s, SIZE_T z) {
- X while (z != 0)
- X s[--z] = 0;
- X}
- X
- X/* zero out the fifo queue, removing the fifos from /tmp as you go (also prints errors arising from signals) */
- X
- Xvoid empty_fifoq() {
- X int sp;
- X
- X while (fifoq != NULL) {
- X unlink(fifoq->w);
- X wait(&sp);
- X statprint(sp);
- X fifoq = fifoq->n;
- X }
- X}
- X
- XSIZE_T strarraylen(char **a) {
- X SIZE_T i;
- X
- X for (i = 0; *a != NULL; a++)
- X i += strlen(*a) + 1;
- X
- X return i;
- X}
- END_OF_FILE
- if test 5186 -ne `wc -c <'utils.c'`; then
- echo shar: \"'utils.c'\" unpacked with wrong size!
- fi
- # end of 'utils.c'
- fi
- if test -f 'var.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'var.c'\"
- else
- echo shar: Extracting \"'var.c'\" \(5802 characters\)
- sed "s/^X//" >'var.c' <<'END_OF_FILE'
- X/* var.c: provide "public" functions for adding and removing variables from the symbol table */
- X
- X#include "rc.h"
- X#include "utils.h"
- X#include "hash.h"
- X#include "list.h"
- X#include "footobar.h"
- X#include "nalloc.h"
- X#include "status.h"
- X#include "glom.h"
- X
- X#ifdef READLINE /* need to reset readline() every time TERM or TERMCAP changes */
- Xextern void rl_reset_terminal(char *);
- X#endif
- X
- Xstatic void colonassign(char *, List *, boolean);
- Xstatic void listassign(char *, List *, boolean);
- Xstatic int hasalias(char *);
- X
- Xstatic char *const aliases[] = {
- X "home", "HOME", "path", "PATH", "cdpath", "CDPATH"
- X};
- X
- X/* assign a variable in List form to a name, stacking if appropriate */
- X
- Xvoid varassign(char *name, List *def, boolean stack) {
- X Variable *new;
- X List *newdef = listcpy(def); /* important to do the listcpy first; get_var_place() frees old values */
- X
- X new = get_var_place(name, stack);
- X new->def = newdef;
- X new->extdef = NULL;
- X
- X#ifdef READLINE /* need to reset readline() every time TERM or TERMCAP changes */
- X if (interactive && streq(name, "TERM") || streq(name, "TERMCAP"))
- X rl_reset_terminal(NULL);
- X#endif
- X}
- X
- X/* assign a variable in string form. Check to see if it is aliased (e.g., PATH and path) */
- X
- Xboolean varassign_string(char *extdef) {
- X char *name = get_name(extdef);
- X Variable *new;
- X int i;
- X static boolean aliasset[arraysize(aliases)] = {
- X FALSE, FALSE, FALSE, FALSE, FALSE, FALSE
- X };
- X
- X if (name == NULL)
- X return FALSE; /* add it to bozo env */
- X
- X i = hasalias(name);
- X if (i >= 0) {
- X aliasset[i] = TRUE;
- X if ((i & 1 == 0) && aliasset[i^1])
- X return TRUE; /* don't alias variables that are already set in upper case */
- X }
- X new = get_var_place(name, FALSE);
- X new->def = NULL;
- X new->extdef = ealloc(strlen(extdef) + 1);
- X strcpy(new->extdef, extdef);
- X if (hasalias(name) != -1)
- X alias(name, varlookup(name), FALSE);
- X return TRUE;
- X}
- X
- X/*
- X Return a List based on a name lookup. If the list is in external (string) form,
- X convert it to internal (List) form. Treat $n (n is an integer) specially as $*(n).
- X Also check to see if $status is being dereferenced. (we lazily evaluate the List
- X associated with $status)
- X*/
- X
- XList *varlookup(char *name) {
- X Variable *look;
- X List *ret, *l;
- X int sub;
- X
- X if (streq(name, "status"))
- X return sgetstatus();
- X
- X if (*name != '\0' && (sub = a2u(name)) != -1) { /* handle $1, $2, etc. */
- X for (l = varlookup("*"); l != NULL && sub != 0; --sub)
- X l = l->n;
- X if (l == NULL)
- X return NULL;
- X ret = nnew(List);
- X ret->w = l->w;
- X ret->m = NULL;
- X ret->n = NULL;
- X return ret;
- X }
- X
- X look = lookup_var(name);
- X
- X if (look == NULL)
- X return NULL; /* not found */
- X if (look->def != NULL)
- X return look->def;
- X if (look->extdef == NULL)
- X return NULL; /* variable was set to null, e.g., a=() echo foo */
- X
- X ret = parse_var(name, look->extdef);
- X
- X if (ret == NULL) {
- X look->extdef = NULL;
- X return NULL;
- X }
- X return look->def = ret;
- X}
- X
- X/* lookup a variable in external (string) form, converting if necessary. Used by makeenv() */
- X
- Xchar *varlookup_string(char *name) {
- X Variable *look;
- X
- X look = lookup_var(name);
- X
- X if (look == NULL)
- X return NULL;
- X if (look->extdef != NULL)
- X return look->extdef;
- X if (look->def == NULL)
- X return NULL;
- X return look->extdef = list2str(name, look->def);
- X}
- X
- X/* remove a variable from the symtab. "stack" determines whether a level of scoping is popped or not */
- X
- Xvoid varrm(char *name, boolean stack) {
- X int i = hasalias(name);
- X
- X if (streq(name, "*") && !stack) { /* when assigning () to $*, we want to preserve $0 */
- X varassign("*", varlookup("0"), FALSE);
- X return;
- X }
- X
- X delete_var(name, stack);
- X if (i != -1)
- X delete_var(aliases[i^1], stack);
- X}
- X
- X/* assign a value (List) to a variable, using array "a" as input. Used to assign $* */
- X
- Xvoid starassign(char *dollarzero, char **a, boolean stack) {
- X List *s, *var;
- X
- X var = nnew(List);
- X var->w = dollarzero;
- X
- X if (*a == NULL) {
- X var->n = NULL;
- X varassign("*", var, stack);
- X return;
- X }
- X
- X var->n = s = nnew(List);
- X
- X while (1) {
- X s->w = *a++;
- X if (*a == NULL) {
- X s->n = NULL;
- X break;
- X } else {
- X s->n = nnew(List);
- X s = s->n;
- X }
- X }
- X varassign("*", var, stack);
- X}
- X
- X/* (ugly name, huh?) assign a colon-separated value to a variable (e.g., PATH) from a List (e.g., path) */
- X
- Xstatic void colonassign(char *name, List *def, boolean stack) {
- X char *colondef;
- X List dud;
- X SIZE_T deflen;
- X List *r;
- X
- X if (def == NULL) {
- X varassign(name, NULL, stack);
- X return;
- X }
- X
- X deflen = listlen(def) + 1; /* one for the null terminator */
- X
- X colondef = nalloc(deflen);
- X strcpy(colondef, def->w);
- X
- X for (r = def->n; r != NULL; r = r->n) {
- X strcat(colondef, ":");
- X strcat(colondef, r->w);
- X }
- X
- X dud.w = colondef;
- X dud.n = NULL;
- X varassign(name, &dud, stack);
- X}
- X
- X/* assign a List variable (e.g., path) from a colon-separated string (e.g., PATH) */
- X
- Xstatic void listassign(char *name, List *def, boolean stack) {
- X List *val, *r;
- X char *v, *w;
- X
- X if (def == NULL) {
- X varassign(name, NULL, stack);
- X return;
- X }
- X
- X v = def->w;
- X
- X r = val = enew(List);
- X
- X while((w = strchr(v,':')) != NULL) {
- X *w = '\0';
- X r->w = ecpy(v);
- X *w = ':';
- X v = w + 1;
- X r->n = enew(List);
- X r = r->n;
- X }
- X r->w = ecpy(v);
- X r->n = NULL;
- X
- X varassign(name, val, stack);
- X}
- X
- X/* check to see if a particular variable is aliased; return -1 on failure, or the index */
- X
- Xstatic int hasalias(char *name) {
- X int i;
- X
- X for (i = 0; i < arraysize(aliases); i++)
- X if (streq(name, aliases[i]))
- X return i;
- X return -1;
- X}
- X
- X/* alias a variable to its lowercase equivalent. function pointers are used to specify the conversion function */
- X
- Xvoid alias(char *name, List *s, boolean stack) {
- X int i = hasalias(name);
- X static void (*vectors[])(char *, List *, boolean) = {
- X varassign, varassign, colonassign, listassign, colonassign, listassign
- X };
- X
- X if (i != -1)
- X vectors[i](aliases[i^1], s, stack); /* xor hack to reverse case of alias entry */
- X}
- END_OF_FILE
- if test 5802 -ne `wc -c <'var.c'`; then
- echo shar: \"'var.c'\" unpacked with wrong size!
- fi
- # end of 'var.c'
- fi
- if test -f 'which.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'which.c'\"
- else
- echo shar: Extracting \"'which.c'\" \(2968 characters\)
- sed "s/^X//" >'which.c' <<'END_OF_FILE'
- X/* which.c: check to see if a file is executable.
- X
- X This function is isolated from the rest because of #include trouble with
- X ANSI compilers.
- X
- X This function was originally written with Maarten Litmaath's which.c as
- X a template, but was changed in order to accomodate the possibility of
- X rc's running setuid or the possibility of executing files not in the
- X primary group. Much of this file has been re-vamped by Paul Haahr.
- X I re-re-vamped the functions that Paul supplied to correct minor bugs
- X and to strip out unneeded functionality.
- X*/
- X
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <sys/param.h>
- X#include "builtins.h"
- X
- X#define M_USR 0700
- X#define M_GRP 0070
- X#define M_OTH 0007
- X#define X_ALL 0111
- X
- X#ifndef NULL
- X#define NULL 0
- X#endif
- X
- Xtypedef struct List {
- X char *w;
- X char *m;
- X struct List *n;
- X} List;
- X
- Xextern int stat(const char *, struct stat *);
- Xextern int geteuid(void);
- Xextern int getegid(void);
- Xextern int getgroups(int, int *);
- Xextern char *strcpy(char *, char *);
- Xextern char *strcat(char *, char *);
- Xextern int strlen(char *);
- X
- Xextern List *varlookup(char *);
- Xextern void *ealloc(int);
- Xextern void efree(void *);
- Xextern int isabsolute(char *);
- X
- Xstatic int initialized = 0;
- Xstatic int uid, gid;
- X
- X#ifdef NGROUPS
- Xstatic int ngroups, gidset[NGROUPS];
- X
- X/* determine whether gid lies in gidset */
- X
- Xstatic int ingidset(int gid) {
- X int i;
- X for (i = 0; i < ngroups; i++)
- X if (gid == gidset[i])
- X return 1;
- X return 0;
- X}
- X#endif
- X
- X/*
- X A home-grown access/stat. Does the right thing for group-executable files.
- X Returns a boolean result instead of this -1 nonsense.
- X*/
- X
- Xstatic int rc_access(char *path) {
- X struct stat st;
- X int mask;
- X
- X if (stat(path, &st) != 0)
- X return 0;
- X
- X mask = X_ALL;
- X
- X if (uid != 0) {
- X if (uid == st.st_uid)
- X mask &= M_USR;
- X#ifdef NGROUPS
- X else if (gid == st.st_gid || ingidset(st.st_gid))
- X#else
- X else if (gid == st.st_gid)
- X#endif
- X mask &= M_GRP;
- X else
- X mask &= M_OTH;
- X }
- X
- X return (st.st_mode & S_IFMT) == S_IFREG && (st.st_mode & mask) == mask;
- X}
- X
- X/* return a full pathname by searching $path, and by checking the status of the file */
- X
- Xchar *which(char *name) {
- X static char *test = NULL;
- X static int testlen = 0;
- X List *path;
- X int len;
- X
- X if (name == NULL) /* no filename? can happen with "> foo" as a command */
- X return NULL;
- X
- X if (!initialized) {
- X initialized = 1;
- X uid = geteuid();
- X gid = getegid();
- X#ifdef NGROUPS
- X ngroups = getgroups(NGROUPS, gidset);
- X#endif
- X }
- X
- X if (isabsolute(name)) /* absolute pathname? */
- X return rc_access(name) ? name : NULL;
- X
- X len = strlen(name);
- X for (path = varlookup("path"); path != NULL; path = path->n) {
- X int need = strlen(path->w) + len + 2; /* one for null terminator, one for the '/' */
- X if (testlen < need) {
- X efree(test);
- X test = ealloc(testlen = need);
- X }
- X if (*path->w == '\0') {
- X strcpy(test, name);
- X } else {
- X strcpy(test, path->w);
- X strcat(test, "/");
- X strcat(test, name);
- X }
- X if (rc_access(test))
- X return test;
- X }
- X return NULL;
- X}
- END_OF_FILE
- if test 2968 -ne `wc -c <'which.c'`; then
- echo shar: \"'which.c'\" unpacked with wrong size!
- fi
- # end of 'which.c'
- fi
- echo shar: End of archive 3 \(of 4\).
- cp /dev/null ark3isdone
- MISSING=""
- for I in 1 2 3 4 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 4 archives.
- rm -f ark[1-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
- exit 0 # Just in case...
- --
- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM
- Sterling Software, IMD UUCP: uunet!sparky!kent
- Phone: (402) 291-8300 FAX: (402) 291-4362
- Please send comp.sources.misc-related mail to kent@uunet.uu.net.
-